<?php
###########################################################################
#  Copyright (C) 2006-2007 Glyphix, Inc. <briz@glyphix.com>               #
#                                                                         #
#  Permission is hereby granted, free of charge, to any person obtaining  #
#  a copy of this software and associated documentation files (the        #
#  "Software"), to deal in the Software without restriction, including    #
#  without limitation the rights to use, copy, modify, merge, publish,    #
#  distribute, sublicense, and/or sell copies of the Software, and to     #
#  permit persons to whom the Software is furnished to do so, subject to  #
#  the following conditions:                                              #
#                                                                         #
#  The above copyright notice and this permission notice shall be         #
#  included in all copies or substantial portions of the Software.        #
#                                                                         #
#  Except as contained in this notice, the name(s) of the above           #
#  copyright holders shall not be used in advertising or otherwise to     #
#  promote the sale, use or other dealings in this Software without       #
#  prior written authorization.                                           #
#                                                                         #
#  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        #
#  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     #
#  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. #
#  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR      #
#  OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,  #
#  ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR  #
#  OTHER DEALINGS IN THE SOFTWARE.                                        #
###########################################################################

/**
 * GXError
 *
 * GXPage registers this object as PHP's internal error reporting method.
 * @author		Brad Brizendine <briz@glyphix.com>
 * @link		http://www.glyphix.com/
 * @license		http://opensource.org/licenses/bsd-license.php BSD License
 * @version		1.0
 * @package		GXPage
 * @subpackage	Engine
 * @copyright	Copyright 2006 - 2007, Glyphix, Inc.
 */

class GXError extends Exception {

	// firephp
	private $firephp = null;

	// stores errors and messages
	private $trace = array();

	// lookup values for error codes
	private $errType = array (
		0		=> 'None',
		1		=> 'Error',
		2		=> 'Warning',
		4		=> 'ParsingError',
		8		=> 'Notice',
		16		=> 'CoreError',
		32		=> 'CoreWarning',
		64		=> 'CompileError',
		128		=> 'CompileWarning',
		256		=> 'UserError',
		512		=> 'UserWarning',
		1024	=> 'UserNotice',
		2048	=> 'RunTime',
		4096	=> 'Recoverable',
		8191	=> 'AllErrors',
		8192	=> 'Deprecated'
		);
	// level to record errors
	private $logLevel = 0;

	// increments each time a message arrives
	private $index = 0;

	// object var to ensure only one instance of this object
	private static $instance = null;
	// method to call to instantiate this class
	public static function getInstance( $className = __CLASS__ ){
		if( self::$instance == null )
			self::$instance = new $className();
		return self::$instance;
	}

	// when object is created, call set_error_handler
	public function __construct( $conf = null, $lang = null ){
		set_error_handler( array(&$this,'handleError'), E_ALL );
		parent::__construct($conf,$lang);
		if( is_file(dirname(__FILE__) .'/FirePHPCore/FirePHP.class.php') ){
			require_once( dirname(__FILE__) .'/FirePHPCore/FirePHP.class.php' );
			$this->firephp = FirePHP::getInstance(true);
			$this->firephp->log('FirePHP instantiated');
		}
	}

	// set the code equal to and above to record errors
	public function setLevel( $code = null ){
		$this->logLevel = $code;
		return $this->errType[ $code ];
	}

	// sets the level at which a log item will be discarded
	public function setLogLevel( $level = 0 ){
		if( !$this->errType[$level] ){
			trigger_error( 'Invalid level specified; use 1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8191; setting level to 256', E_USER_WARNING );
			$level = 0;
		}
		$this->logLevel = intval($level);
		return true;
	}

	/**
	 * handleError
	 *
	 * Receives errors directly from PHP.
	 * @param integer $code the numeric code for the error severity
	 * @param string $msg the error
	 * @param string $file the path to the file where the error originated
	 * @param integer $line the line which threw the error
	 * @param string $context code surrounding the error
	 * @return boolean
	 */
	public function handleError( $code = null, $msg = null, $file = null, $line = null, $context = null ){
		// if we want to record this error... (make sure we always record UserNotice)
		if( ($this->logLevel > 0 && $code >= $this->logLevel) || $code == 1024 ){

			// handle DOM core-level errors, since DOM doesn't throw exceptions on its own
			if( (is_numeric(strpos($msg,'DOMDocument::'))) ){
				throw new DOMException($msg);
			}

			// get the path info so that we can get the basename instead of the entire path
			$pathInfo = pathinfo($file);
			// build the error array
			$e = array(
				'index'		=> $this->index++,
				'code'		=> $code,
				'type'		=> $this->errType[ $code ],
				'msg'		=> $msg,
				'file'		=> $pathInfo['basename'],
				'line'		=> $line
				//'context'	=> $context		// generates huge log files, so turning it off
				);
			$this->trace[] = $e;

			// if we have firephp installed, use it and return
			if($this->firephp){
				if(!is_scalar($msg)){
					$msg = var_dump($msg, true);
				}
				switch(true){
					case in_array($code, array(1,4,16,32,64,256,2048,8191)):
						$this->firephp->error($msg, $file .'(' .$line .')');
						break;
					case in_array($code, array(2,32,128,512,4096)):
						$this->firephp->warn($msg, $file .'(' .$line .')');
						break;
					case in_array($code, array(8,1024)):
						$this->firephp->info($msg, $file .'(' .$line .')');
						break;
					default:
						$this->firephp->log($msg, $file .'(' .$line .')');
				}
				return true;
			}
		}

		// otherwise, log all errors
		return $this->log( $code, $msg .' in ' .$file .'(' .$line .')' );
	}

	/**
	 * log
	 *
	 * adds a log item in different ways based on the code
	 * @param integer $code the numeric value of the error level
	 * @param string $txt the textual message to log
	 * @return boolean false to force PHP to stop, true to allow it to continue
	 */
	private function log( $code = null, $txt = null ){
		// log all errors using PHP's default settings unless logLevel is zero
		if( $this->logLevel > 0 && $code >= $this->logLevel ){
			error_log($this->errType[$code] .' :: ' .$txt);
		}

		// return true if it's a user error, false if it's a php error
		if( $code >= 256 ){
			return true;
		}
		return false;
	}

	/**
	 * getErrors
	 *
	 * if @code is numeric, return all errors greater >= code
	 * if it's a string, return just that branch
	 * @param mixed $code string or number
	 * @return array
	 */
	public function getErrors( $code = null ){
		switch(true){
			case is_numeric($code):
				for( $i = 0; $i < count($this->trace); $i++ ){
					if( $this->trace[$i]['code'] >= $code ){
						$list[] = $this->trace[$i];
					}
				}
				return $list;
				break;
			case is_string($code):
				return $this->trace[ $code ];
				break;
			default:
				return $this->trace;
		}
	}

}

?>